/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor.ext;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.BadLocationException;
import org.netbeans.editor.Acceptor;
import org.netbeans.editor.AcceptorFactory;
import org.netbeans.editor.Analyzer;
import org.netbeans.editor.BaseKit;
import org.netbeans.editor.EditorDebug;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Formatter;
import org.netbeans.editor.FinderFactory;
import org.netbeans.editor.Utilities;
import org.netbeans.editor.SyntaxSupport;
import org.netbeans.editor.Settings;
import org.netbeans.editor.SettingsUtil;
import org.netbeans.editor.TextBatchProcessor;
import org.netbeans.editor.Syntax;
import org.netbeans.editor.SettingsChangeEvent;
/**
* Base formatter providing basic support for finding indentation
* and creating writers
*
* @author Miloslav Metelka
* @version 1.00
*/
public class BaseFormatter extends Formatter {
protected Resolver[] resolvers = new Resolver[0];
protected char[] hotChars = Analyzer.EMPTY_CHAR_ARRAY;
private Class kitClass;
protected void settingsChange(SettingsChangeEvent evt, Class kitClass) {
super.settingsChange(evt, kitClass);
this.kitClass = kitClass; // store it locally here
}
/** Create format writer. This is the primary method to be overriden
* in children to get the appropriate format writer created.
*/
protected Writer createFormatWriter(Document doc, Syntax syntax,
Writer underWriter, int startIndent, boolean atLineStart) {
return new BaseFormatWriter(this, syntax, underWriter,
startIndent, atLineStart);
}
public Writer createWriter(Document doc, int offset, Writer writer) {
int startIndent = 0;
boolean atLineStart = false;
Syntax syntax;
try {
if (doc instanceof BaseDocument) {
BaseDocument d = (BaseDocument)doc;
syntax = d.createSyntax();
startIndent = Math.max(findAnyIndent(d, offset), 0);
atLineStart = (Utilities.getRowStart(d, offset) == offset);
} else {
syntax = BaseKit.getKit(kitClass).createSyntax(null);
}
return createFormatWriter(doc, syntax, writer, startIndent, atLineStart);
} catch (BadLocationException e) {
e.printStackTrace();
}
return writer;
}
/** Find the indentation for the line given by pos.
* @param doc document
* @param pos position on the line
* @return indentation of the given line or -1 if indentation not resolved
*/
protected int findIndent(BaseDocument doc, final int pos)
throws BadLocationException {
final FinderFactory.CharArrayBwdFinder finder
= new FinderFactory.CharArrayBwdFinder(hotChars);
final boolean dbg = ((debugMode & DEBUG_FIND_INDENT) != 0);
if (dbg) {
System.out.println("findIndent(): Entering with pos=" + pos // NOI18N
+ ", hotChars='" + EditorDebug.debugChars(hotChars) + "'"); // NOI18N
}
final SyntaxSupport sup = doc.getSyntaxSupport();
TextBatchProcessor tbp = new TextBatchProcessor() {
public int processTextBatch(BaseDocument doc2, int startPos,
int endPos, boolean lastBatch) {
try {
int[] commentBlocks = doc2.getSyntaxSupport().getCommentBlocks(endPos, startPos);
if (dbg) {
System.out.println("findIndent(): Area computed: [" + startPos + ", " // NOI18N
+ endPos + "] or [" + Utilities.debugPosition(doc2, startPos) // NOI18N
+ ", " + Utilities.debugPosition(doc2, endPos) + "]\n" // NOI18N
+ "Blocks:\n" + EditorDebug.debugBlocks(doc2, commentBlocks)); // NOI18N
}
int lastPos = startPos;
while ((lastPos = doc2.find(finder, lastPos, endPos)) != -1) {
if (!Analyzer.blocksHit(commentBlocks, lastPos, lastPos + 1)) {
char hotChar = finder.getFoundChar();
if (dbg) {
System.out.println("findIndent(): Found hotChar '" + hotChar // NOI18N
+ "' at pos=" + lastPos + " or " + Utilities.debugPosition(doc2, lastPos)); // NOI18N
}
// go through resolvers to find the ones with correct hotChar
for (int i = 0; i < resolvers.length; i++) {
if (resolvers[i].getHotChar() == hotChar) {
if (dbg) {
System.out.println("Found resolver '" + resolvers[i].getName() // NOI18N
+ "' for hotChar='" + hotChar + "'"); // NOI18N
}
int indent = resolvers[i].resolve(doc2, pos, lastPos);
if (indent != -1) {
if (dbg) {
System.out.println("findIndent(): Found indent=" + indent // NOI18N
+ " by resolver '" + resolvers[i].getName() + "'"); // NOI18N
}
return indent; // request stop
}
}
}
}
}
} catch (BadLocationException e) {
e.printStackTrace();
}
return -1; // continue the search
}
};
return doc.processText(tbp, pos, 0);
}
/** First call findIndent() method and if it doesn't return
* indent, then try to get indent by the previous line indent.
* If this fails, return -1.
*/
protected int findAnyIndent(BaseDocument doc, int pos)
throws BadLocationException {
int indent = findIndent(doc, pos);
if (indent < 0) {
int prevBolPos = Math.max(Utilities.getRowStart(doc, pos, -1), 0);
indent = Utilities.getRowIndent(doc, prevBolPos, false);
}
return indent;
}
/** Round the computed indentation to the multiply
* of the shiftwidth.
*/
protected int roundIndent(int indent) {
int shw = getShiftWidth();
return indent / shw * shw;
}
private void updateResList(int ind, Resolver r, boolean add) {
ArrayList rlist = new ArrayList(Arrays.asList(resolvers));
if (add) {
rlist.add(ind, r);
} else {
rlist.remove(ind);
}
resolvers = (Resolver[])rlist.toArray(resolvers);
updateHotChars();
}
public void addResolver(Resolver r) {
updateResList(resolvers.length, r, true);
}
public void addResolver(int ind, Resolver r) {
updateResList(ind, r, true);
}
public void removeResolver(int ind) {
updateResList(ind, null, false);
}
public String debugResolvers() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < resolvers.length; i++) {
sb.append("[" + i + "]: "); // NOI18N
sb.append(resolvers[i].getName());
sb.append('\n');
}
sb.append("\nDescriptions:\n"); // NOI18N
for (int i = 0; i < resolvers.length; i++) {
Resolver r = resolvers[i];
sb.append(r.getName());
sb.append(": "); // NOI18N
sb.append(r.getDesc());
sb.append("\n\n"); // NOI18N
}
return sb.toString();
}
private char[] addHotChar(char ch, char[] hots) {
for (int i = 0; i < hots.length; i++) {
if (hots[i] == ch) {
return hots; // already in
}
}
char[] tmp = new char[hots.length + 1];
System.arraycopy(hots, 0, tmp, 0, hots.length);
tmp[hots.length] = ch;
return tmp;
}
private void updateHotChars() {
char[] hots = Analyzer.EMPTY_CHAR_ARRAY;
for (int i = 0; i < resolvers.length; i++) {
char ch = resolvers[i].getHotChar();
int j;
for (j = 0; j < hots.length; j++) {
if (hots[j] == ch) {
break;
}
}
if (j == hots.length) { // not there
hots = addHotChar(ch, hots);
}
}
hotChars = hots;
}
/** Layer that tries to resolve the indentation for a given line.
* If it doesn't succeed by returning -1, the next resolver in
* the resolver array is attempted.
*/
public interface Resolver {
/** Get the name of this resolver */
public String getName();
/** Get the description of this resolver */
public String getDesc();
/** Get the hot character for this resolver */
public char getHotChar();
/** Try to resolve the indentation and find the indentation
* @param doc document to work with
* @param pos original position for which the indentation is being searched
* @param hotCharPos position where the hot char was found
*/
public int resolve(BaseDocument doc, int pos, int hotCharPos);
}
// Debugging
public int debugMode; // debugging of the formatter actions
public static final int DEBUG_FIND_INDENT = 1;
public static final int DEBUG_FORMAT = 2;
protected final boolean dbgFI() {
return ((debugMode & DEBUG_FIND_INDENT) != 0);
}
protected final boolean dbgF() {
return ((debugMode & DEBUG_FORMAT) != 0);
}
}
/*
* Log
* 10 Gandalf-post-FCS1.8.1.0 3/8/00 Miloslav Metelka
* 9 Gandalf 1.8 1/13/00 Miloslav Metelka Localization
* 8 Gandalf 1.7 1/6/00 Miloslav Metelka
* 7 Gandalf 1.6 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 6 Gandalf 1.5 9/10/99 Miloslav Metelka
* 5 Gandalf 1.4 8/27/99 Miloslav Metelka
* 4 Gandalf 1.3 7/29/99 Miloslav Metelka
* 3 Gandalf 1.2 7/26/99 Miloslav Metelka
* 2 Gandalf 1.1 7/21/99 Miloslav Metelka
* 1 Gandalf 1.0 7/9/99 Miloslav Metelka
* $
*/